#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <pthread.h>

/* 	Calculating the pi value using Gauss-Legendre method	
	ATTENTION: we strongly recommend to see the sequential version of this code first (gauss-legendre-seq.c) 	*/


/* 	these funcions is used by each of the threads, which calculates the operations	*/
	
//a1 = (a + b) / 2.0;
void * op1(void * args)
{
	double * args_d = (double *) args;
	args_d[4] = (args_d[0] + args_d[1])/2;
	
	/* the function must return something - NULL will do */
	return NULL;
}

//b1 = sqrt(a * b);
void * op2(void * args)
{
	double * args_d = (double *) args;
	args_d[5] = sqrt(args_d[0] * args_d[1]);

	return NULL;
}

//p1 = 2.0 * p; 
void * op3(void * args)
{
	double * args_d = (double *) args;
	args_d[7] = 2 * args_d[3];

	return NULL;
}

//t1 = t - p * (a - a1) * (a - a1);
void * op4(void * args)
{
	double * args_d = (double *) args;
	args_d[6] = args_d[2] - args_d[3] * (args_d[0] - args_d[4]) * (args_d[0] - args_d[4]);

	return NULL;
}

int main(int argc, char ** argv)
{
	if(argc != 2) {
		printf("Usage: ./gaus-legendre-par <number_of_iteractions>\n");
		exit(1);
	}

	/* 	initializating the variables	*/

	//	number of iteractions
	int n = atoi(argv[1]);	

	//	all terms used by the gauss-legendre method
	double a = 1.0, b = 1.0 / sqrt(2), t = 0.25, p = 1.0;
	double a1, b1, t1, p1;
	pthread_t pt1;
	pthread_t pt2;
	pthread_t pt3;
	pthread_t pt4;
	
	double args[8];

	// how the vector represents our variables of the sequential version
	args[0] = a;
	args[1] = b;
	args[2] = t;
	args[3] = p;
	args[4] = a1;
	args[5] = b1;
	args[6] = t1;
	args[7] = p1;

	int i;
	for(i = 0; i < n; i++) {

		//	for each iteraction calculate all new terms of the sequence

		// 	create the threads that can run in paralel (these operations are independents)
		if(pthread_create(&pt1, NULL, op1, args))
			fprintf(stderr, "Error creating thread 1\n"), exit(1);
		if(pthread_create(&pt2, NULL, op2, args))
			fprintf(stderr, "Error creating thread 2\n"), exit(1);
		if(pthread_create(&pt3, NULL, op3, args))
			fprintf(stderr, "Error creating thread 3\n"), exit(1);


		//	waits for the first thread to finish, so we can procede the calculation
		if(pthread_join(pt1, NULL))
			fprintf(stderr, "Error joining thread 1\n"), exit(1);

		//	create the last thread (for the fourth operation) which depends of the 'a1' term (operation/thread 1)
		if(pthread_create(&pt4, NULL, op4, args))
			fprintf(stderr, "Error creating thread 4\n"), exit(1);

		//	waits for the rest of the threads, so we can follow to the next terms of the sequence
		if(pthread_join(pt2, NULL))
			fprintf(stderr, "Error joining thread 2\n"), exit(1);
		if(pthread_join(pt3, NULL))
			fprintf(stderr, "Error joining thread 3\n"), exit(1);
		if(pthread_join(pt4, NULL))
			fprintf(stderr, "Error joining thread 4\n"), exit(1);

		//	a = a1, b = b1, t = t1, p = p1;
		args[0] = args[4];
		args[1] = args[5];
		args[2] = args[6];
		args[3] = args[7];
	}
		
	//	at the end, calculate the value of Pi
	//	double pi = ((a1 + b1) * (a1 + b1)) / (4.0 * t1);
	double pi = ((args[4] + args[5]) * (args[4] + args[5])) / (4.0 * args[6]);
	printf("%.7lf\n", pi);

	return 0;
}
